/* Copyright (c) 2006-2012, DNSPod Inc.
 * All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the FreeBSD Project.
 */
  

#include "storage.h"
#include "..\wydns\storage.h"


const uint MAX_ELE_NUM = 1000000;


//////////////////////////memory chunk///////////////////////////
//push to tail
//pop from head
 msgcache *
init_msgcache(int n)
{
     msgcache *p_msg = NULL;
    int pgsz;
    if (n < 1 || n > 5000)      //page size == 4k. 5000*4k == 20m
        return NULL;
    pgsz = getpagesize();
    if ((p_msg = malloc(sizeof( msgcache) + pgsz * n)) == NULL)
        return NULL;
    p_msg->size = pgsz * n;
    pthread_spin_init(&p_msg->lock, 0);
    p_msg->head = p_msg->tail = 0;
    p_msg->pkt = 0;
    return p_msg;
}


void
free_msgcache( msgcache *p_msg)
{
    if (p_msg != NULL)
        free(p_msg);
}

int pop_msgcache(msgcache* p_msg, mbuf_type** pp_mbuf)
{
	pthread_spin_lock(&p_msg->lock);
	if (p_msg->pkt == 0) {
		pthread_spin_unlock(&p_msg->lock);
		return - 1;
	}
/*	printf("pkt --  %d\n", p_msg->pkt);*/
	memcpy(pp_mbuf, p_msg->data + p_msg->head, sizeof(void *));
	p_msg->head = p_msg->head + sizeof(void *);//sizeof( seninfo) + se->len;
	if (p_msg->head + 8 > p_msg->size)
		p_msg->head = 0;
	p_msg->pkt--;
	pthread_spin_unlock(&p_msg->lock);
	return 0;

}

/////////////////////////////////////////////////////////////////


int
get_mvalue_len(uchar * val)
{
     mvalue *p_val = ( mvalue *) val;
    return p_val->len;
}


//this should be defined in upper files
int
ttl_expired(uchar * val)
{
     mvalue *p_val = ( mvalue *) val;
    uint now = global_now;
    if (p_val->ttl == (MAX_TTL + 1))       //never expired
        return 0;
    if (p_val->ttl < now)
        return 1;
    return 0;
}


//use by memory hash and disk db
//copy data from storage to buffer
//after this, operate data will not need
//to lock database any more
static int
deep_copy(uchar * from, uchar * to, int tlen)   //*to is big enough
{
     mvalue *p_val = ( mvalue *) from;
    int size = p_val->len + sizeof( mvalue) + p_val->seg * sizeof(uint16_t);
    if (size >= tlen)
        return -1;
    p_val->hits++;
    //printf("size is %d\n",size);
    memcpy(to, from, size);
    return size;
}


/////////////////////////memory hash/////////////////////////////
uint
get_hash_table_index(void *argv, int len, hashval_t *hash)
{
    if (*hash == 0)
        *hash = nocase_char_hash_function(argv, len);
	return get_hash_table_index_by_hash(*hash);
}


uint get_hash_table_index_by_hash(hashval_t hash) 
{
	return (hash / MULTI_HASH) % MULTI_HASH;
}

 htable_t *
creaate_hash_table(hashfunc * hash_func, comparefunc * compare_func, int size, int num)
{
    int i, j;
     htable_t *ht = NULL;
    if (compare_func == NULL)
        return NULL;
    if ((ht = malloc(sizeof( htable_t) * num)) == NULL)
        return NULL;
    for (i = 0; i < num; i++) {
        ht[i].h = hash_func;
        if (hash_func == NULL)
            ht[i].h = nocase_char_hash_function;
        ht[i].c = compare_func;
        ht[i].size = size;
        ht[i].edata = NULL;
        ht[i].now = 0;          //no need lock
        ht[i].mask = size - 1;
        pthread_spin_init(&(ht[i].lock), 0);
        if ((ht[i].table =
             malloc(sizeof( hdata_t) * ht[i].size)) == NULL) {
            for (j = 0; j < i; j++)
                free(ht[j].table);
            free(ht);
            return NULL;
        }
        for (j = 0; j < size; j++) {
            ht[i].table[j].list = NULL;
            pthread_spin_init(&(ht[i].table[j].lock), 0);
        }
    }
    return ht;
}

void clean_val_from_hash_entry( hentry_t *p_hentry, uint32_t limit,  rbtree *p_rbtree, int ttl_update)
{
     mvalue *p_val;
    int i, val_num = SUPPORT_TYPE_NUM;
    uchar *val;
    time_t now = global_now;
     ttlnode tt_node, *p_ttnode;
     rbnode *p_rbnode;
    
    assert(p_hentry->count > 0);
    
    for (i = 0; i< val_num; i++)
    {
        val = p_hentry->vals[i];
        if (val == NULL)
            continue;
        
        p_val = ( mvalue *)val;
        if ((p_val->ttl > (now + ttl_update + 1)) && (p_val->hits < limit))
        {
            tt_node.data = p_hentry->key;
            tt_node.type = support_type[i];
            tt_node.exp = p_val->ttl;
            tt_node.dlen = strlen((const char *)p_hentry->key) + 1;
            tt_node.lowerdomain = NULL;
            pthread_spin_lock(&p_rbtree->lock);
            p_rbnode = find_node(p_rbtree, &tt_node);
            if (p_rbnode != NULL)
            {
                p_ttnode = delete_node(p_rbtree, p_rbnode);
                if (p_ttnode != NULL)
                {
                    //printf("delete true\n");
                    free(p_ttnode->lowerdomain);
                    free(p_ttnode);
                } else
                    printf("delete error\n");
            }
            else
            {
                /* printf("find error\n"); */
                /* dbg_print_td(key); */
            }
            pthread_spin_unlock(&p_rbtree->lock);
                    
            free(val);
            p_hentry->vals[i] = NULL;
            p_hentry->count--;
        }
        
        if (0 == p_hentry->count)
            break;
    }
    
    return;
}

int
clean_emtry_hase_entry( htable_t *p_htable, int index,/* int off, uchar * buffer,
               uchar * key, int *klen, int vlen,*/ uint32_t limit,
                rbtree *p_rbtree, int ttl_update)
{
    int /*ret,*/ debug = DEBUG_TIMES;
     hdata_t *p_hdata;
     hentry_t *p_hentry, *prev = NULL, *tmp;
    
    if (index > HASH_TABLE_SIZE)
        return -1;
    p_hdata = p_htable->table + index;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return -1;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        clean_val_from_hash_entry(p_hentry, limit, p_rbtree, ttl_update);
        if (0 == p_hentry->count)
        {
            tmp = p_hentry;
            if (NULL == prev)
                p_hdata->list = p_hentry->next;
            else
                prev->next = p_hentry->next;
            p_hentry = p_hentry->next;
            free(tmp);
            p_hdata->now--;
            pthread_spin_lock(&p_htable->lock);
            p_htable->now--;
            pthread_spin_unlock(&p_htable->lock);
        }
        else
        {
            prev = p_hentry;
            p_hentry = p_hentry->next;
        }
        debug--;
        if (debug == 0) {
            printf("error in storage...\n");
            exit(0);
        }
    }
    pthread_spin_unlock(&p_hdata->lock);
    return -1;
}

uchar *get_val_from_he( hentry_t *p_hentry, int type)
{
    uchar *old_val;
    
    assert(p_hentry->count > 0);
    
    switch (type)
    {
        case A:
            old_val = p_hentry->val.A;
            break;
        case NS:
            old_val = p_hentry->val.NS;
            break;
        case CNAME:
            old_val = p_hentry->val.CNAME;
            break;
        case SOA:
            old_val = p_hentry->val.SOA;
            break;
        case MX:
            old_val = p_hentry->val.MX;
            break;
        case TXT:
            old_val = p_hentry->val.TXT;
            break;
        case AAAA:
            old_val = p_hentry->val.AAAA;
            break;
        case SRV:
            old_val = p_hentry->val.SRV;
            break;
        case PTR:
            old_val = p_hentry->val.PTR;
            break;
        default:
            old_val = NULL;
            break;
    }
    
    return old_val;
}

// read dirty
int
find_val( htable_t *p_htable, uchar * key, int klen, int type, uchar * buffer, int buff_len,
             mvalue *p_val, hashval_t *hash)
{
    int idx, debug = DEBUG_TIMES, ret;
     hdata_t *p_hdata = NULL;
     hentry_t *p_hentry = NULL;
    uchar *val;
    
    if (*hash == 0)
        *hash = (p_htable->h) (key, klen);
    idx = *hash & p_htable->mask;
    p_hdata = p_htable->table + idx;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return -1;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        if ((p_htable->c) (key, p_hentry->key) == 0) {
            val = get_val_from_he(p_hentry, type);
            if (NULL == val)
                ret = -1;
            else if (buffer != NULL)
                ret = deep_copy(val, buffer, buff_len);
            else {
                if (p_val != NULL) {
					*p_val = *(( mvalue *)val);
                }
                ret = 1;        //successed
            }
            pthread_spin_unlock(&p_hdata->lock);
            return ret;
        }
        p_hentry = p_hentry->next;
        if (debug-- == 0) {
            printf("error in htable find\n");
            exit(0);
        }
    }
    pthread_spin_unlock(&p_hdata->lock);
    return -1;
}

int
find_list_io_from_he( hentry_t *p_hentry, int *typeoff, uchar **buffer)
{
    int i, val_num = SUPPORT_TYPE_NUM;
    uchar *val;
    
    assert(p_hentry->count > 0);
    
    for (i = *typeoff; i < val_num; i++)
    {
        val = p_hentry->vals[i];
        if (val == NULL)
            continue;
        
        *buffer = val;
        *typeoff = i;
        return 1;
    }
    
    return 0;
}

int
get_next_val_with_offset( htable_t *p_htable, int index, int off, int *typeoff, uchar **buffer)
{
    int debug = DEBUG_TIMES, ret;
     hdata_t *p_hdata = NULL;
     hentry_t *p_hentry = NULL;
    
    p_hdata = p_htable->table + index;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return -1;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        if (off == 0) {
            ret = find_list_io_from_he(p_hentry, typeoff, buffer);
            pthread_spin_unlock(&p_hdata->lock);
            return ret;
        }
        off--;
        p_hentry = p_hentry->next;
        if (debug-- == 0) {
            printf("error in htable find list io\n");
            exit(0);
        }  
    }
    pthread_spin_unlock(&p_hdata->lock);
    return -1;
}

int
get_list_val_from_he( hentry_t *p_hentry, int typeoff, uchar **buffer)
{
    uchar *val;
    
    assert(p_hentry->count > 0);
    
    val = p_hentry->vals[typeoff];
    *buffer = val;
    if (NULL == val)
        return -1;
    return 1;
}

int
htable_find_list( htable_t *p_htable, uchar *key, int typeoff, int index, uchar **buffer)
{
    int debug = DEBUG_TIMES/*, ret*/;
     hdata_t *p_hdata = NULL;
     hentry_t *p_hentry = NULL;
    mbuf_type *p_mbuf;
    
    p_hdata = p_htable->table + index;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return -1;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        p_mbuf = (mbuf_type *)(p_hentry->vals[typeoff]);
        if ((p_mbuf != NULL) && ((p_htable->c) (key, p_mbuf->qing) == 0)) {
            *buffer = (uchar *)p_mbuf;
            pthread_spin_unlock(&p_hdata->lock);
            return 1;
        }
        p_hentry = p_hentry->next;
        if (debug-- == 0) {
            printf("error in htable find\n");
            exit(0);
        }
    }
    pthread_spin_unlock(&p_hdata->lock);
    return -1;
}

uchar *delete_val_from_he( hentry_t *p_hentry, int type)
{
    uchar **old_val, *val = NULL;
    
    assert(p_hentry->count > 0);
    
    switch (type)
    {
        case A:
            old_val = &(p_hentry->val.A);
            break;
        case NS:
            old_val = &(p_hentry->val.NS);
            break;
        case CNAME:
            old_val = &(p_hentry->val.CNAME);
            break;
        case SOA:
            old_val = &(p_hentry->val.SOA);
            break;
        case MX:
            old_val = &(p_hentry->val.MX);
            break;
        case TXT:
            old_val = &(p_hentry->val.TXT);
            break;
        case AAAA:
            old_val = &(p_hentry->val.AAAA);
            break;
        case SRV:
            old_val = &(p_hentry->val.SRV);
            break;
        case PTR:
            old_val = &(p_hentry->val.PTR);
            break;
        default:
            return NULL;
            break;
    }
    
    if (*old_val != NULL)
    {
        val = *old_val;
        *old_val = NULL;
        p_hentry->count--;
    }
    
    return val;
}

uchar *
delete_val_from_hash_entry( htable_t *p_htable, uchar * key, int klen, int type, hashval_t hash)
{
    hashval_t h = (hash) ? (hash) : ((p_htable->h) (key, klen));
    int index = h & p_htable->mask, debug = DEBUG_TIMES;
     hdata_t *p_hdata = NULL;
     hentry_t *p_hentry = NULL, *prev = NULL;
    p_hdata = p_htable->table + index;
    uchar *val;
    
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return NULL;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        if ((p_htable->c) (key, p_hentry->key) == 0) {
            val = delete_val_from_he(p_hentry, type);
            if (0 == p_hentry->count)
            {
                if (NULL == prev)
                    p_hdata->list = p_hentry->next;
                else
                    prev->next = p_hentry->next;
                free(p_hentry);
                p_hdata->now--;
                pthread_spin_lock(&p_htable->lock);
                p_htable->now--;
                pthread_spin_unlock(&p_htable->lock);
            }
            pthread_spin_unlock(&p_hdata->lock);
            return val;
        }
        prev = p_hentry;
        p_hentry = p_hentry->next;
        debug--;
        if (debug == 0) {
            printf("error in storage\n");
            exit(0);
        }
    }
    pthread_spin_unlock(&p_hdata->lock);
    return NULL;
}

uchar *remove_val_from_entry( hentry_t *p_hentry, int typeoff)
{
    uchar **old_val, *val = NULL;
    
    assert(p_hentry->count > 0);
    
    old_val = &(p_hentry->vals[typeoff]);
    
    if (*old_val != NULL)
    {
        val = *old_val;
        *old_val = NULL;
        p_hentry->count--;
    }
    
    return val;
}

// uchar *
// remove_entry_list( htable *p_htable, int typeoff, int index, int off)
// {
//     int debug = DEBUG_TIMES;
//      hdata *p_hdata = NULL;
//      hentry *p_hentry = NULL, *prev = NULL;
//     uchar *val;
//     
//     p_hdata = p_htable->table + index;
//     pthread_spin_lock(&p_hdata->lock);
//     if (p_hdata->list == NULL) {
//         pthread_spin_unlock(&p_hdata->lock);
//         return NULL;
//     }
//     p_hentry = p_hdata->list;
//     while (p_hentry != NULL) {
//         if (off == 0) {
//             val = remove_val_from_entry(p_hentry, typeoff);
//             if (0 == p_hentry->count)
//             {
//                 if (NULL == prev)
//                     p_hdata->list = p_hentry->next;
//                 else
//                     prev->next = p_hentry->next;
//                 free(p_hentry);
//                 p_hdata->now--;
//                 pthread_spin_lock(&p_htable->lock);
//                 p_htable->now--;
//                 pthread_spin_unlock(&p_htable->lock);
//             }
//             pthread_spin_unlock(&p_hdata->lock);
//             return val;
//         }
//         off--;
//         prev = p_hentry;
//         p_hentry = p_hentry->next;
//         if (debug-- == 0) {
//             printf("error in htable find list io\n");
//             exit(0);
//         }  
//     }
//     pthread_spin_unlock(&p_hdata->lock);
//     return NULL;
// }

uchar *
remove_val_with_offset( htable_t *p_htable, uchar *key, int typeoff, int index)
{
    int debug = DEBUG_TIMES;
     hdata_t *p_hdata = NULL;
     hentry_t *p_hentry = NULL, *prev = NULL;
    uchar *val;
    
    p_hdata = p_htable->table + index;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL) {
        pthread_spin_unlock(&p_hdata->lock);
        return NULL;
    }
    p_hentry = p_hdata->list;
    while (p_hentry != NULL) {
        if ((p_htable->c) (key, p_hentry->key) == 0) {
            val = remove_val_from_entry(p_hentry, typeoff);
            if (0 == p_hentry->count)
            {
                if (NULL == prev)
                    p_hdata->list = p_hentry->next;
                else
                    prev->next = p_hentry->next;
                free(p_hentry);
                p_hdata->now--;
                pthread_spin_lock(&p_htable->lock);
                p_htable->now--;
                pthread_spin_unlock(&p_htable->lock);
            }
            pthread_spin_unlock(&p_hdata->lock);
            return val;
        }
        prev = p_hentry;
        p_hentry = p_hentry->next;
        if (debug-- == 0) {
            printf("error in htable find list io\n");
            exit(0);
        }  
    }
    pthread_spin_unlock(&p_hdata->lock);
    return NULL;
}


// A, NS, CNAME, SOA, MX, TXT, AAAA, SRV, PTR
int append_value_to_he( hentry_t *p_hentry, uchar *val, int type, int is_replace,
                         mvalue *p_val)
{
    int ret;
    uchar **old_val;
    
    switch (type)
    {
        case A:
            old_val = &(p_hentry->val.A);
            break;
        case NS:
            old_val = &(p_hentry->val.NS);
            break;
        case CNAME:
            old_val = &(p_hentry->val.CNAME);
            break;
        case SOA:
            old_val = &(p_hentry->val.SOA);
            break;
        case MX:
            old_val = &(p_hentry->val.MX);
            break;
        case TXT:
            old_val = &(p_hentry->val.TXT);
            break;
        case AAAA:
            old_val = &(p_hentry->val.AAAA);
            break;
        case SRV:
            old_val = &(p_hentry->val.SRV);
            break;
        case PTR:
            old_val = &(p_hentry->val.PTR);
            break;
        default:
            return -1;
            break;
    }
    
    if (*old_val != NULL)
    {
        if (is_replace)
        {
            if (p_val != NULL)     //get old meta data
                *p_val = *( mvalue *) (*old_val);
            if ((p_val != NULL) && (p_val->ttl != (MAX_TTL + 1)))
            {
                /*
                 * 如果替换的为NS类型，则TTL使用原有NS记录的TTL
                 * 否则授权与根返回的NS不一致时，会无法刷新NS
                 */
                if (NS == type)
                {
                    (( mvalue *)val)->ttl = p_val->ttl;
                }
				if (((mvalue*)old_val)->hits>= 1000)
				{
					((mvalue *)val)->hits = ((mvalue*)old_val)->hits;
				}
                free(*old_val);
                *old_val = val;
                ret = HTABLE_INSERT_RET_REPLACE;
            }
            else
            {
                ret = HTABLE_INSERT_RET_NEVER_EXPIRE;
            }
        }
        else
        {
            ret = HTABLE_INSERT_RET_NO_REPLACE;
        }
    }
    else
    {
        p_hentry->count++;
        *old_val = val;
        ret = HTABLE_INSERT_RET_NORMAL;
    }
    return ret;
}

//if conllision, replace old element by default.
//if replace is 1, replace it, return 1
//if replace is 0, drop it, return -1
//else return 0
int
insert__mvalue_to_hash_table( htable_t *p_htable, uchar * key, int klen, int type, uchar * in_val, int is_replace,
               mvalue *mv, hashval_t *hashd)
{
    int idx, ret, debug = DEBUG_TIMES;
     hentry_t *he = NULL, *cl = NULL;
     hdata_t *hd = NULL;
    /*  mvalue *pt = NULL;   //protect root and gtld */
    uchar dlen = klen;
    he = malloc(sizeof( hentry_t) + dlen);
    if (he == NULL) {
        printf("oom\n");
        return -1;
    }
    memset(he, 0, sizeof( hentry_t));
    memcpy(he->key, key, dlen);
    if (*hashd == 0) {
        *hashd = p_htable->h(key, klen);
    }
    idx = *hashd & p_htable->mask;
    //printf("hash %u,idx is %d\n",hash,idx);
    hd = p_htable->table + idx;
    pthread_spin_lock(&hd->lock);
    if (hd->list == NULL)
    {
        append_value_to_he(he, in_val, type, is_replace, NULL);
        hd->now = 1;
        hd->list = he;
    }
    else {
        cl = hd->list;
        while (cl != NULL) {
            if ((p_htable->c) (cl->key, he->key) == 0)        //the exactly same elements
            {
                ret = append_value_to_he(cl, in_val, type, is_replace, mv);
                pthread_spin_unlock(&hd->lock);
                free(he);
                return ret;     //replace
            }
            cl = cl->next;
            debug--;
            if (debug == 0) {
                printf("error in storage2\n");
                exit(0);
            }
        }
        append_value_to_he(he, in_val, type, is_replace, NULL);
        he->next = hd->list;
        hd->list = he;
        hd->now++;
    }
    pthread_spin_unlock(&hd->lock);
    pthread_spin_lock(&p_htable->lock);
    p_htable->now++;
    pthread_spin_unlock(&p_htable->lock);
    return HTABLE_INSERT_RET_NORMAL;
}

int
install_mbuf_to_hash_table( htable_t *p_htable, uchar * key, int klen, int type, uchar * val, int is_replace,
               mvalue *p_val, hashval_t *hash)
{
    int idx, ret, debug = DEBUG_TIMES;
     hentry_t *p_hentru = NULL, *cl = NULL, *prev = NULL;
     hdata_t *p_hdata = NULL;
    /*  mvalue *pt = NULL;   //protect root and gtld */
    p_hentru = malloc(sizeof( hentry_t) + klen);
    if (p_hentru == NULL) {
        printf("oom\n");
        return -1;
    }
    memset(p_hentru, 0, sizeof( hentry_t));
    memcpy(p_hentru->key, key, klen);
    if (*hash == 0) {
        *hash = p_htable->h(key, klen);
    }
    idx = *hash & p_htable->mask;
    //printf("hash %u,idx is %d\n",hash,idx);
    p_hdata = p_htable->table + idx;
    pthread_spin_lock(&p_hdata->lock);
    if (p_hdata->list == NULL)
    {
        append_value_to_he(p_hentru, val, type, is_replace, NULL);
        p_hdata->now = 1;
        p_hdata->list = p_hentru;
    }
    else {
        cl = p_hdata->list;
        while (cl != NULL) {
            if ((p_htable->c) (cl->key, p_hentru->key) == 0)        //the exactly same elements
            {
                ret = append_value_to_he(cl, val, type, is_replace, p_val);
                pthread_spin_unlock(&p_hdata->lock);
                free(p_hentru);
                return ret;     //replace
            }
            prev = cl;
            cl = cl->next;
            debug--;
            if (debug == 0) {
                printf("error in storage3\n");
                exit(0);
            }
        }
        append_value_to_he(p_hentru, val, type, is_replace, NULL);
        prev->next = p_hentru;
        p_hdata->now++;
    }
    pthread_spin_unlock(&p_hdata->lock);
    pthread_spin_lock(&p_htable->lock);
    p_htable->now++;
    pthread_spin_unlock(&p_htable->lock);
    return 0;
}


int
find_val_with_ttl( htable_t *p_htable, uchar * key, int klen, int type, uchar *in_val, int vlen,
                      mvalue *out_val, hashval_t *hash)
{
    int idx, ret;
    uchar *old_val;
    idx = get_hash_table_index(key, klen, hash);
    ret = find_val(p_htable + idx, key, klen, type, in_val, vlen, out_val, hash);
    if (ret > 0) {
        if (ttl_expired(in_val) == 1) {
            old_val = delete_val_from_hash_entry(p_htable + idx, key, klen, type, *hash);
            if (old_val != NULL)
                free(old_val);
        } else {
            return ret;
        }
    }
    return -1;
}


/////////////////////////////////////////////////////////////////
////////////////////////////////dbg//////////////////////////////
// #define THREADX (5)
// #define NUMX (10000)
//  st_hlp {
//      htable *ht;
//     int idx;
// };
// 
// 
// void *
// st_th(void *arg)
// {
//     int i, idx;
//     uchar key[50] = { 0 };
//     int klen;
//     uchar *val = NULL;
//     int pre = 0;
// //      hentry *he = NULL;
//     uchar *oval;
//      htable *ht;
//      st_hlp *sh = ( st_hlp *) arg;
//     hashval_t hash;
//     idx = sh->idx;
//     ht = sh->ht;
//     for (i = idx * NUMX; i < (idx + 1) * NUMX; i++) {
//         hash = 0;
//         sprintf((char *)key, "%dkey", i);
//         val = malloc(50);
//         sprintf((char *)val, "%dval", i);
//         //printf("%d,%s,%s\n",idx,key,val);
//         klen = strlen((const char *)key) + 1;
//         pre = get_pre_mem_hash(key, klen, &hash);
//         htable_insert(ht + pre, key, klen, A, val, 0, NULL, &hash);
//     }
//     if (idx == (THREADX - 1))
//         idx = -1;
//     sleep(2);
//     for (i = (idx + 1) * NUMX; i < (idx + 2) * NUMX; i++) {
//         hash = 0;
//         sprintf((char *)key, "%dkey", i);
//         klen = strlen((const char *)key) + 1;
//         pre = get_pre_mem_hash(key, klen, &hash);
//         oval = htable_delete(ht + pre, key, klen, A, hash);
//         if (oval == NULL) {
//             printf("error in test %s,%d,%d\n", key, idx, i);
//         }
//         else
//             free(oval);
//     }
//     sleep(5);
//     return NULL;
// }
// 
// 
// int
// storage_test(void)
// {
//      htable *ht = NULL;
//     pthread_t pt[THREADX];
//     int i;
//      st_hlp sh[THREADX];
//     //ht = htable_create(NULL,dict_comp_str_equ,HASH_TABLE_SIZE,MULTI_HASH);
//     if (ht == NULL)
//         dns_error(0, "create htable error");
//     for (i = 0; i < THREADX; i++) {
//         sh[i].ht = ht;
//         sh[i].idx = i;
//         if (pthread_create(pt + i, NULL, st_th, sh + i))
//             dns_error(0, "create pthread");
//     }
//     for (i = 0; i < THREADX; i++)
//         pthread_join(pt[i], NULL);
//     sleep(2);
//     return 0;
// }


